#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>
#include "socket.hpp"
#include "protocol.hpp"

void useTips(char *command)
{
    std::cout << "useTips:" << command << " ip" << " port" << std::endl;
}

void *sendRequest(void *args)
{
    unique_ptr<socket_ns::Socket> *pclt = static_cast<unique_ptr<socket_ns::Socket> *>(args);
    while (true)
    {
        sleep(1);
        // 1、生产请求
        protocol_ns::Factory fac;
        shared_ptr<protocol_ns::Request> req = fac.productRequest();

        // 2、格式转换 + 序列化
        string jsonstr = "";
        req->serialized(jsonstr);

        // 3、添加协议报头
        string packstr = protocol_ns::encoding(jsonstr);
        cout << "packstr:" << packstr << endl;

        // 4、发送报文
        (*pclt)->Send(packstr);
    }
    return nullptr;
}

void *recvResponse(void *args)
{
    unique_ptr<socket_ns::Socket> *pclt = static_cast<unique_ptr<socket_ns::Socket> *>(args);
    while (true)
    {
        sleep(1);
        // 5、接收报文
        string packstr;
        (*pclt)->Recv(packstr);

        while (packstr.size()) //一次处理一个完整的报文
        {
            // 6、解包
            string jsonstr;
            jsonstr = protocol_ns::decoding(packstr);
            if (jsonstr.empty())
                break;

            // 7、反序列化 + json格式转自定义格式
            //./udpclient 127.0.0.1 8899 2> /dev/pts/6，调用客户端的时候，使用该命令，将标准错误流输出重定向到/dev/pts/6设备
            fprintf(stderr, "jsonstr: %s\n", jsonstr.c_str());
            protocol_ns::Response res;
            res.nonSerialized(jsonstr);
        }
    }
    return nullptr;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        useTips(argv[0]);
        exit(1);
    }

    // 1、创建套接字，并连接服务器
    struct sockaddr_in destaddr;
    destaddr.sin_family = AF_INET;
    destaddr.sin_addr.s_addr = inet_addr(argv[1]);
    destaddr.sin_port = htons(stoi(argv[2]));
    unique_ptr<socket_ns::Socket> clt = make_unique<socket_ns::TcpSocket>();
    clt->buildClientSocket((struct sockaddr *)&destaddr);

    // 2、通讯：创建两个线程，一个一直发送请求，一个一直接收结果
    pthread_t tid1;
    pthread_create(&tid1, nullptr, sendRequest, &clt); // 发送

    pthread_t tid2;
    pthread_create(&tid2, nullptr, recvResponse, &clt); // 接收

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);

    return 0;
}
